
import java.util.*;

public class World extends Thread {

    public static final int WIDTH = 100;
    public static final int HEIGHT = 100;
    private double[][][] world;//[x][y][cost, pheremone_A, resource_A,pheremone_B,resource_B]
    public final int COST_INDEX = 0;
    public final int PHEREMONE_A_INDEX = 1;
    public final int RESOURCE_A_INDEX = 2;
    public final int PHEREMONE_B_INDEX = 3;
    public final int RESOURCE_B_INDEX = 4;
    public final int COLONY_X;
    public final int COLONY_Y;
    private double decayRate_A;
    private double decayRate_B;
    private List<Ant> ants = new ArrayList<Ant>();
    private int current_A;
    private int current_B;
    private int gathered_A;
    private int gathered_B;
    private int total = 0;
    private Individual ind;
    private int NUM_A_ANTS;
    private int NUM_B_ANTS;
    private HashSet<Pair>[] trails = new HashSet[2];

    public World(Individual ind) {
        this.ind = ind;
        this.decayRate_A = ind.getDecay_A_Rate();
        this.decayRate_B = ind.getDecay_B_Rate();
        gathered_A = -1 * ind.getNum_A_Ants() * ind.getAnt_A_Cost();
        gathered_B = -1 * ind.getNum_B_Ants() * ind.getAnt_B_Cost();

        trails[0] = new HashSet<Pair>();
        trails[1] = new HashSet<Pair>();

        COLONY_X = WIDTH / 2;
        COLONY_Y = HEIGHT / 2;
        world = new double[WIDTH][HEIGHT][5];
        for (int w = 0; w < WIDTH; w++) {
            for (int h = 0; h < HEIGHT; h++) {
                world[w][h][COST_INDEX] = (int) (Math.random() * 5 + 1);

                //high variability
                //world[w][h][COST_INDEX] = (int) (Math.random() * 15 + 1);

                //columns

                /* if (w % 8 < 4) {
                world[w][h][COST_INDEX] = (int) (Math.random() * 3 + 1);
                } else {
                world[w][h][COST_INDEX] = (int) (Math.random() * 8 + 5);
                }*/

                //rows
                /*if (h % 8 < 4) {
                world[w][h][COST_INDEX] = (int) (Math.random() * 3 + 1);
                } else {
                world[w][h][COST_INDEX] = (int) (Math.random() * 8 + 5);
                }*/

                //hole

                /*if (w > COLONY_X - 50 && w < COLONY_X + 50 && h > COLONY_Y - 50 && h > COLONY_Y + 50) {
                world[w][h][COST_INDEX] = (int) (Math.random() * 3 + 1);
                } else {
                world[w][h][COST_INDEX] = (int) (Math.random() * 5 + 5);
                }*/

            }
        }
        scatterResources(10, 25);

        NUM_A_ANTS = ind.getNum_A_Ants();
        for (int i = 0; i < NUM_A_ANTS; i++) {
            add(new Ant(ind, COLONY_X, COLONY_Y, 'a'));
        }


        NUM_B_ANTS = ind.getNum_B_Ants();
        for (int i = 0; i < NUM_B_ANTS; i++) {
            add(new Ant(ind, COLONY_X, COLONY_Y, 'b'));
        }
    }

    public double[][][] getWorld() {
        return world;
    }

    public int getColonyX() {
        return COLONY_X;
    }

    public int getColonyY() {
        return COLONY_Y;
    }

    public int getPopulation() {
        return ants.size();
    }

    public int getTotal() {
        return total;
    }

    public int getCurrent_A() {
        return current_A;
    }

    public int getCurrent_B() {
        return current_B;
    }

    public int getGathered_A() {
        return gathered_A;
    }

    public int getGathered_B() {
        return gathered_B;
    }

    public void returnResources_A() {
        gathered_A++;
        current_A++;
    }

    public void returnResources_B() {
        gathered_B++;
        current_B++;
    }

    void scatterResources(int numPiles, int maxResource) {
        for (int i = 0; i < numPiles * 2; i++) {
            int x = (int) (Math.random() * (WIDTH - 5) + 3);
            int y = (int) (Math.random() * (HEIGHT - 5) + 3);
            int index;
            int resources = maxResource;
            if (Math.random() < .5) {
                index = RESOURCE_A_INDEX;
                resources *= 1.5;
            } else {
                index = RESOURCE_B_INDEX;

            }
            resources = (int) (Math.random() * resources);
            total += (int) (resources * 2.5);
            world[x][y][index] = resources;
            world[x - 1][y][index] = resources / 2;
            world[x + 1][y][index] = resources / 2;
            world[x][y - 1][index] = resources / 2;
            world[x][y + 1][index] = resources / 2;
        }
    }

    void add(Ant ant) {
        ants.add(ant);
        ant.setLocation(this);
    }

    void decayTrails() {
        ArrayList<Pair> toRemove = new ArrayList();

        for (Pair p : trails[0]) {
            int x = p.getX();
            int y = p.getY();

            world[x][y][PHEREMONE_A_INDEX] -= decayRate_A;
            if (world[x][y][PHEREMONE_A_INDEX] <= 0) {
                world[x][y][PHEREMONE_A_INDEX] = 0;
                toRemove.add(p);
            }


        }
        trails[0].removeAll(toRemove);
        toRemove = new ArrayList();

        for (Pair p : trails[1]) {
            int x = p.getX();
            int y = p.getY();

            world[x][y][PHEREMONE_B_INDEX] -= decayRate_B;
            if (world[x][y][PHEREMONE_B_INDEX] <= 0) {
                world[x][y][PHEREMONE_B_INDEX] = 0;
                toRemove.add(p);
            }

        }
        trails[1].removeAll(toRemove);
    }

    void increase_A_Trail(int x, int y) {
        trails[0].add(new Pair(x, y));
        world[x][y][PHEREMONE_A_INDEX]++;
    }

    void increase_B_Trail(int x, int y) {
        trails[1].add(new Pair(x, y));
        world[x][y][PHEREMONE_B_INDEX]++;
    }

    boolean takeResource_A(int x, int y) {
        if (world[x][y][RESOURCE_A_INDEX] >= 1) {
            world[x][y][RESOURCE_A_INDEX]--;
            return true;
        } else {
            return false;
        }

    }

    boolean takeResource_B(int x, int y) {
        if (world[x][y][RESOURCE_B_INDEX] >= 1) {
            world[x][y][RESOURCE_B_INDEX]--;
            return true;
        } else {
            return false;
        }

    }

    public List<Ant> getAnts() {
        return ants;
    }

    public void run() {

        while (true) {
            if (ants.isEmpty()) {
                ind.reportFitness(getGathered_A() + getGathered_B(), getTotal());
                System.out.println("resources gathered: " + (getGathered_A() + getGathered_B()) + "/" + getTotal() + ":" + ind.getID());

                return;
            }
            decayTrails();

            for (Ant ant : ants) {
                ant.move();
            }

            int i = 0;
            while (i < ants.size()) {
                if (ants.get(i).shouldDie()) {
                    ants.remove(i);
                } else {
                    i++;
                }
            }

            //cross A and B for costs, etc
            if (current_B > ind.getAnt_A_Cost() && Math.random() < ind.getProbabilityOfAnt_A_Child()) {
                World.this.add(new Ant(ind, COLONY_X, COLONY_Y, 'a'));
                current_B -= ind.getAnt_A_Cost();
            }
            if (current_A > ind.getAnt_B_Cost() && Math.random() < ind.getProbabilityOfAnt_B_Child()) {
                World.this.add(new Ant(ind, COLONY_X, COLONY_Y, 'b'));
                current_A -= ind.getAnt_B_Cost();
            }
        }

    }
}
